//
// Created by denglibin on 2021/9/9.
//

#include "03_listen_file.h"
#include "init.h"
#include "log.h"
#include <stdlib.h>
#ifndef _WIN32
#include <fcntl.h>
#include <sys/types.h>
#include<unistd.h>
#include <sys/stat.h>
#include <errno.h>
#endif

/**
 * 事件回调
 * @param fd
 * @param event
 * @param arg
 */
static void read_file(evutil_socket_t fd, short event, void* arg);

static void read_file(evutil_socket_t fd, short event, void* arg){
    //zlog_info(log_get_zlog_c(), "read file...");
    char buf[1024] = {0};
    int len;
    while ((len = read(fd, buf, sizeof(buf) - 1)) > 0){
        printf("%s\n", buf);
        memset(buf, 0, len);
    }
}

/**
 * 创建配置
 * @return
 */
static struct event_config * get_event_config(){
    //创建配置
    struct event_config * conf = event_config_new();

#ifdef _WIN32
    //windows下使用iocp, 内部使用线程池
    event_config_set_flag(conf, EVENT_BASE_FLAG_STARTUP_IOCP);
    //初始化iocp的线程
    int r = evthread_use_windows_threads();
    if(r != 0){
        fprintf(stderr, "evthread_use_windows_threads() fail!");
        return NULL;
    }
    SYSTEM_INFO si;
    GetSystemInfo(&si); //获取系统信息
    //设置cpu数量
    event_config_set_num_cpus_hint(conf, si.dwNumberOfProcessors);

#endif

    //设置特征(windows不支持)
    //event_config_require_features(conf,  EV_FEATURE_ET);
    //设置支持文件描述符,设置后不支持epoll
    event_config_require_features(conf, EV_FEATURE_FDS);
    return conf;
}

static  struct event_base * get_event_base(){
    struct event_config* conf = get_event_config();
    if(conf == NULL){
        zlog_info(log_get_zlog_c(),"get_event_config() fail!");
        return NULL;
    }
    //使用配置初始化上下文
    struct event_base * base = event_base_new_with_config(conf);
    //释放
    event_config_free(conf);
    if(!base) {
        zlog_info(log_get_zlog_c(), "event_base_new_with_config fail!");
        return NULL;
    }
    //显示支持的网络模式
    const char** methods = event_get_supported_methods();
    //windows -> win32
    //linux -> epoll,poll, select (epoll优先，然后poll,最后select)
    for(int i = 0; methods[i] != NULL; i++) {
        zlog_info(log_get_zlog_c(),"support method: %s", methods[i]);
    }

    //获取当前网络模型 window->win32 linux->epoll
    const char* method = event_base_get_method(base);
    zlog_info(log_get_zlog_c(), "current method: %s", method);

    //确认特征是否生效(windows下都不行)
    int f = event_base_get_features(base);
    if(f & EV_FEATURE_ET) { //边沿触发
        zlog_info(log_get_zlog_c(),"EV_FEATURE_ET supported");
    }
    if(f & EV_FEATURE_O1){
        zlog_info(log_get_zlog_c(),"EV_FEATURE_O1 supported");
    }
    if(f & EV_FEATURE_FDS){
        zlog_info(log_get_zlog_c(),"EV_FEATURE_FDS supported");
    }
    if(f & EV_FEATURE_EARLY_CLOSE){
        zlog_info(log_get_zlog_c(),"EV_FEATURE_EARLY_CLOSE supported");
    }
    return base;
}

void demo_3(){
#ifndef _WIN32
    init();
    struct event_base * base = get_event_base();
    if(base == NULL){
        zlog_error(log_get_zlog_c(),"get_event_base fail!");
        clean();
        return;
    }
    //只读非阻塞打开文件
    int fd = open("demo3.txt", O_RDONLY|O_NONBLOCK, 0);
    if(fd <= 0){
        perror("open demo3.txt");
        zlog_error(log_get_zlog_c(), "open demo3.txt fail");
        clean();
        return;
    }

    //移动文件结尾
    lseek(fd, 0, SEEK_END);
    struct event* fev = event_new(base, fd, EV_READ|EV_PERSIST, read_file,  NULL);
    int r = event_add(fev, NULL);
    if(r != 0 ){
        zlog_error(log_get_zlog_c(), "event_add fail");
        clean();
        return;
    }
    //事件分发处理
    zlog_info(log_get_zlog_c(),"event_base_dispatch...");
    event_base_dispatch(base);
    event_base_free(base);
    close(fd);
    clean();
#endif
}
